本篇读书笔记的内容大部分是来自我刚参与的掘金的读书打卡活动。这次读书打卡活动选择了《简约之美:软件设计之道》这本书。我把这几次打卡的内容作了整合,也加了一点没有在打卡活动里面写的内容构成了这篇读书笔记。
本书一共有九个章节,我抽取了10个觉得比较好的段落并附加上我个人的解读。希望读者可以有些收获。
追求卓越还是得过且过
“程序中复杂的部分必须以某种简单的方式组织起来。。。这就是编程所要用到的艺术和才能 - 化繁为简。
‘差程序员’是不会化繁为简的。他们总以为编程语言“能跑通”的程序,就已经化解了复杂性,而没考虑降低其他程序员需要面对的复杂性。。。在这些程序员看来,他们写出来的东西是能用的,这就是他们的老板需要的,看来也应该是客户需要的。。。他们把结果交给其它程序员,其他程序员又会在这之上增添复杂性,完成自己的工作。
程序员对化解复杂性考虑得越少,程序就越难懂。。。所以,‘好程序员’应当竭尽全力,把程序写得让其他程序员容易理解。因为他写的东西都很好懂,所以要找出bug是相当容易的。
– 来自《简约之美:软件设计之道》”
这里提到的化繁为简,我认为就是将复杂的系统以清晰的结构与高可读性的代码呈现出来的过程。作者认为不能仅仅为了完成需求来写代码,这是一种应付老板和客户的行为。他认为写代码的时候还要考虑到代码是否可以让人容易理解,当然也包括是否易修改,易扩展,易维护。因为随着需求的不断增加,代码的维护成本会越来越高。
其实这本身就是认知高度和自我要求的问题:你是选择追求卓越,还是选择得过且过?我相信两种态度随着时间的推移,所造成影响的差别应该是非常巨大的;不仅是写代码,做任何事情都是。
你的程序有在帮助他人么?
“其实,全部软件都有一个相同的目标:帮助其他人。
文字处理软件帮助大家编辑文档,浏览器帮助大家浏览网页。。。服务于动物或植物的软件,其目的还是帮助人类。。。即使你写的是程序库,它们也是为程序员服务的,程序员也是人。。。软件从来都不是用来帮助无生命事物的。软件要帮助的不是计算机,而是人。
软件的目标不是‘赚钱’或者‘炫耀智商’。。。即便赚钱和炫耀智商可以‘帮助’你,但这只是非常狭隘的帮助;相比为帮助别人而设计的,满足其他人需求的软件,仅仅考虑狭隘目标的设计,很可能收获糟糕的软件。
任何情况下,你所赚的钱都直接维系于你的软件能为他人提供多少帮助。。。。在做与软件有关的决策时,指导法则就是判断能提供什么样的帮助。各项需求也可以照这个标准排除先后顺序。哪项需求能为人们提供最大的帮助,就应当赋予最高的优先级。
所以,软件设计科学的目标应该是:
- 确保软件能提供尽可能多的帮助
- 确保软件能持续提供尽可能多的帮助
- 设计出程序员能尽可能容易开发和维护软件系统,这样的系统才能为用户提供尽可能多的帮助,而且能持续提供尽可能多的帮助
>
– 来自《简约之美:软件设计之道》”
作者认为软件的目的是服务他人,而且软件是否成功在很大程度上取决于软件本身对人的帮助有多大。
这一点很值得程序员,设计,产品经理思考:
现在这个需求或设计是否真的可以帮助我们的目标客户;如果有,帮助大还是小?如果帮助小,那如何改进可以更好地帮助他们?
现在这个代码库是否真的可以帮助我们的业务方?这些功能真的都是可以解决业务方的问题么?是否有些功能是可有可无的?业务方接过来用是否会觉得不方便?或者同事看了我的之后是否能马上理解我代码的意思,不需要花费太多的时间和精力去理解和修改它们?
另外,作者所说的需要可持续提供帮助这一点也非常好。
- 比如这个需求将来能否还有可以做得更好的可能?
- 或者这段代码或者模块扩展性是否很强?是否容易修改?出现问题的时候我们能否积极响应并修复?
我认为这是一个格局的问题:
- 你是只为了挣钱,拿到钱就满足了,还是为了真心想帮助他人,解决他人的问题?
- 你是只为了KPI,完成某个特定任务而已,还是为了真心想帮助同事,帮助公司的客户,让公司越来越好?
格局决定高度。
价值与成本的时间维度
“公式:D = (Vn + Vf)/(Ei + Em)
其中:
- D:可取程度
- Vn:当前价值
- Vf:未来价值
- Ei:实现成本
- Em:维护成本
未来价值和维护成本都取决于时间。。。一般来说,软件系统都需要维护很长时间,大多数情况下,未来长期收益和维护成本才是真正需要考虑的。与之相比,当前价值和实现成本变得无足轻重。
如果我们忽视事实,放弃对未来的思考,只考虑当下“能用”的软件,那么我们软件在未来就会很难维护。如果软件很难维护,就很难确保它能够帮助别人(而这正是软件设计的目标)。
相比短期,长期的未来对我们来说更重要,因为我没打呢的是决策会在未来更长的时间里产生更大的影响。
在软件设计时,可以根据已知的信息做某些决策,目的是为了创造更好的未来(提升价值,降低维护成本),而不必预测未来究竟会发生什么具体的事情。
– 来自《简约之美:软件设计之道》”
很多事情我们都需要把时间一同考虑进去:比如蛀牙会随着时间的推移可能会导致后面整个牙都拔掉,但是有的人缺忽视了时间的作用,一拖再拖导致最后要把整颗牙拔掉;再比如基金可能会长期持有才会有一个比较好的收益,但是有的人在看到一跌再跌就忍不住杀跌,对行业的发展没有关注,更没有想法,而后面再回来看的时候基金又涨回去了。。
软件开发也是如此,我们不仅仅需要解决当下的难题,还要考虑当前的设计在未来的可维护性(也包括可扩展性)。我们不能仅图一时时快,为了应付需求,设计最求短平快,给未来毫不留余地。
我们应该在动手写代码之前要多考虑一下当前设计的可维护性,可扩展性,也就是这个设计在未来会带给我们多大的便利和好处,而不是一步一步让它把我们推向无尽的深渊。
应对变化
“你并不需要预测什么会变化,你需要知道的是,变化必然会发生。程序应该保证尽可能合理的灵活性,这样,不管未来发生什么变化,都可以应付得了。
请回顾整个过程中的每次修改。问问自己,最初写这个文件时,你能预测到这些变化吗?是否一开始写好就能够减轻后期的工作量。总的来说,就是要尝试理解每次修改,看看是否能从中得到一些关于软件开发的新的收获。
渐进式开发及设计:它要求按照特定顺序,一点一点地设计和构建系统。
举一个实现一个支持加减乘除计算器的例子。
- 设计一个只能进行加法运算的系统。
- 修改现有的设计,让它支持减法运算。
- 实现减法运算功能。现在系统支持加法和减法。
- 再次修改现有的设计,让它支持乘法运算。
- 实现乘法运算功能。现在系统支持加法,减法和乘法了。
- 再次修改现有的设计,让它支持除法运算。
- 添加除法的功能。最后得到一个我们期望的可以加减乘除的计算器。
这个方法的精妙之处在于,它是根据实现的顺序来决策的。总的来说,在其中的每个阶段,下一步都只做最容易的事情:首先选择加法,因为这是最简单的运算;其次选择减法,因为从逻辑上说它与加法只有很小的差异;第二步也可以选择乘法,因为乘法无非是把加法执行很多次而已。而唯一不应当选择的是除法,因为从加法到除法的距离太远了,步子太大了。而从乘法到除法却相对简单很多。
这里混合了两种做法:一种叫做‘渐进开发’,另一种叫做‘渐进设计’。
渐进式开发是一种通过该小步骤构建整个系统的办法;渐进式设计也是一种类似的方法,它也通过一系列小步骤来创建和改进系统的设计。– 来自《简约之美:软件设计之道》”
作者指出了我们应该认识到变化是必然会发生的,因此我们在设计系统的时候要尽可能保持灵活,让系统便于修改。而且在每次改动之后我们最好也可以做一个复盘:这个修改我们是否当初想到过,当初的设计是否可以很好地应对这个修改。如果不能,我们以后应该如何避免类似的情况发生?或者我们是否可以积累一下经验,对比变化前后的差异,弥补我们在思考方面的不足?
介绍了渐进式开发和设计:它所追求的是通过小步骤来构建和改进系统的方法,每一步骤相差的距离很小。同时也举了一个简单的例子帮助我们理解。以后在我们构建复杂系统的时候可以借鉴作者介绍的这种渐进式的方案。
这是不是问题?
“永远不要“ 修正” 任何 东西,除非它真的是一个问题,而且有证据表明问题确实存在。
如果相当多的用户认为某个行为是bug,它就是bug;如果只是少数用户(比如一两个)认为它是bug,那么它就不算bug。
在你的程序中,真正需要关注速度的部分,应该局限于你可以证明的、真正让用户体会到有性能问题的那些部分。对程序的其他部分,最主要关心的还是灵活和简洁,而不是速度。
有些开发人员想让速度尽可能快,所以,他们还没弄清楚速度到底慢不慢,就花时间来优化程序。这就好像做慈善事业时,一边把食物送给富人,一边说“我们只是希望帮助他人。
在动手解决之前,真正拿到证据,证明问题确实存在。
– 来自《简约之美:软件设计之道》”
作者强调了在解决问题之前应该先确认这个是不是问题。如果确认了是问题才需要去解决。
- 关于bug:有多数用户反馈的bug才是bug,如果极少数用户反馈的bug,就不应算作bug或者优先级可以排到后面,它不应该属于我们首先解决的问题。
- 关于性能优化:只有可以证明的,用户体会到性能上有问题的部分才需要去优化性能。
其实这就是“把钱用在刀刃上”在软件开发中的实践:我们的开发资源永远是紧张的,因为无论是现在还是未来都要有很多事情要做。因此我们需要解决的是那些明确的,有证据证明的,真正需要解决的问题。
要写傻子也能看懂的程序
“某一部分的代码越简洁,未来进行变化的难度就越低。
如果你的代码写得简洁、自洽,那么修正问题、维护系统就很容易。
要简洁到什么程度呢?
简单到傻子也能看懂!许多程序员在这方面做得尤其差劲。他们以为别人都愿意花很多时间来学习自己写的代码,毕竟这是自己花很多时间写出来的。这些程序员很重视自己的代码,所以对其他人也应当同样重视。没错,程序员通常都是非常聪明的人。但是“噢,其他程序员会理解我写的所有代码,没必要简化或者注释”的看法仍然是不对的。这个问题与智商无关,而与背景知识有关。第一次接触你代码的程序员完全没有任何背景知识,他必须学习。学习的难度越低,找出问题的速度也就越快,使用起来也越容易。
降低代码学习难度的方法有很多:简洁的注释,简单的设计,循序渐进的引导,等等。不过,如果你的代码没有做到傻子都能看懂,其他人学起来就会遇到困难。他们会误解,会制造bug,会把事情搞得一团糟。等这一切发生的时候,他们会找谁?对,就是你。这时候你就得花时间回答他们的各种问题。
– 来自《简约之美:软件设计之道》”
作者提倡我们要写简洁的代码来提高代码的可维护性。还提到了两个方法:用小模块构建系统以及正确使用注释。我们需要尽量做到不需要让别人和未来的自己花费太多成本来读懂代码,这才是对自己负责,对他人负责的态度,因为它符合这本书中提到的“写软件是为了帮助他人”的中心思想。
请保持专注
“你正开发的任何系统,其基本用途应当相当简单。这样开发出来的系统,既满足实际需求,整体来说也是简单的。但是,如果你给系统添加新功能去满足其他目标,事情就立刻变复杂了。
举例来说,文字处理软件的基本功能就是帮助用户写作文档。如果突然要求它能够阅读邮件,最终就会得到的非常荒唐复杂的玩意。我们都知道,这不是文字处理软件本来的用途。这么做,甚至都不是在扩展软件的用途,而是增添与目的无关的功能。
有时候,营销人员或者经理会给软件设定一些目标,但是这些目标其实并不符合程序的基本用途,比如“要好玩一些”、“设计要更有冲击感”、“要受新媒体欢迎”,“要使用最新的技术”等等。这些人可能是公司里的重要人物,但他们不是那些决定程序应当做什么的人。身为软件设计师或是技术经理,你的职责是保证软件的基本用途,防止它偏离正轨,这个责任其他人谁也担不起。
最受用户喜欢的软件是专注而简洁的,并且始终执着于基本用途。
– 来自《简约之美:软件设计之道》”
这段作者呼吁大家在做软件的时候要保持专注,让软件变得纯粹,不要通过试图增加多样化的功能来给软件带来更多复杂性,导致提升后面的维护成本。
因为软件也是产品,它只需要满足特定人群的特定需求就好了,把这一点做到极致即可,而不需要去浪费成本去解决其他的问题。
而且如果是作为软件设计师或者是技术经理,也要有义务保证软件不偏离原有的目标。
这是否是糟糕的技术?
“你可以通过三个因素来判断技术是否“糟糕”:生存潜力、互通性、对品质的重视。
生存潜力:某种技术的生存潜力,就是它持续获得维护的可能性。如果某种类库或依赖项已经过时,没有人维护,你却死守着它们,就有麻烦在等着你了。
接受广泛程度:应当从符合需求的技术中选择接受最广泛的。从某种程度上说这是真的,被接受的技术一般都是有相当大的生存潜力。不过,你还是必须辨别,到底是经过考验才被大家接受,还是仅仅是因为某种垄断而被接受。有些技术之所以被广泛接受,是因为用户暂时别无选择。
互通性:所谓互通性,指的是如果需要,从一种技术切换到另一种技术有多难。要了解技术的互通性,就得问问自己:“我们能不能以某种标准方式来交互,这样就更容易切换到遵循同样标准的其他系统?”
品质:这是一种更主观的衡量,其思想是考察最近的发布中,产品是否更加完善了。如果你可以看到源代码,检查一下开发人员是否进行了重构,清理了代码。产品是更容易使用了,还是更难用了?维护它的人真的在乎产品的质量吗?最近是否多次出现似乎由糟糕的编程所引发的严重安全问题?
还有其他因素需要考虑,主要是它是否简洁,是否符合软件的基本用途。在衡量完所有实际因素之后,也可以考虑个人喜好。
– 来自《简约之美:软件设计之道》”
这段文字给我们在今后做技术选型的时候提供了建议:从生存潜力、互通性、对品质的重视这三个维度来判断选择该项技术是不是可取的,非常有借鉴意义。
应对复杂
如果你设计的是庞大繁杂的模块,那么每一部分都需要花费大量精力,也很难做到足够精致。这样的系统就很难维护,而且必须经常打补丁才可以正常运行。那么,为什么会有人编程时要采用庞大繁杂的模块,而不采用小巧简洁的模块呢?在软件第一次编写时,采用大模块表面上可以节省下相当多的时间。如果使用众多小模块,就必须花很多时间来组合。如果采用大模块则不会这样,零件很少,而且很容易对接。
如果系统中某个部分太过复杂,有个好办法来解决:把它分解成几个独立的小部分,逐步重新设计。每次修改都应该足够小,这样可以放心动手,不会让事情更复杂。
而有时候你受命去解决一些本身就非常复杂的问题,比如编写拼写检查或国际象棋的程序。问题复杂,解法不一定会复杂;相反,在处理此类问题时,你必须更努力地追求代码的简洁。如果你在解决复杂问题时遇到了麻烦,那么用简单易懂的文字把它写在纸上,或者画出来。有些最优秀的程序设计就是在纸上完成的,真的。把它输入到计算机里只是次要的细节。大多数麻烦的设计问题,都可以用在纸上画图或写出来的办法找到答案。
– 来自《简约之美:软件设计之道》”
作者提倡小模块构建系统来降低程序的维护和修改成本。而且在遇到复杂问题的时候,可以将你的设计落在纸上,可以更形象地看到你设计的样子,也更加便于发现问题和修改。
平衡重新设计和新需求开发
“你不能专门花很长的时间来重新设计,停止开发新功能。变化定律告诉我们,程序所处的环境是持续变化的,所以程序的功能也必须去适应这些变化。如果在相当长的时间里,你都不能从用户角度出发来调适和改进,就可能失去自己的用户,把项目弄死。
好在平衡开发新功能和应对复杂性这两项任务的方法有很多。最好的一个办法就是,重新设计时只考虑让新功能更容易实现,然后实现这个功能。这样,你就可以在重新设计和开发新功能之间定期切换。它同样有助于保证新设计能够适应需求,因为设计时会考虑到实际的应用。系统的复杂性也会逐渐下降,而且你一直都跟得上用户的需求。你甚至可以这样来处理bug:如果发现修改设计之后,某些bug更容易修复,那么先重新设计代码再修复bug。
– 来自《简约之美:软件设计之道》”
(一般情况下)我们的软件不断会有新功能,同时,我们也应该让我们的程序越来越好,进行适当的重构和重新设计。那么新功能的开发和重新设计的工作应该如何平衡呢?作者给出了自己的建议:重新设计时只考虑让新功能更容易实现,然后实现这个功能。